
import re
import numpy as np
import json
from typing import Dict, List, Tuple
import cn2an


def divide_blocks(contract_data: Dict)-> Tuple:
    """
    初步划分title_blocks 和 plain_blocks
    """
    text_blocks = contract_data['textBlocks']

    # 第一步：找出可能的title
    title_blocks = dict()
    plain_blocks = dict()
    for block in text_blocks:
        content = block.get('content', '').strip()
        if content == '':
            continue
        block_id = block['id']
        if title_check(content):
            title_blocks[block_id] = block
        else:
            plain_blocks[block_id] = block
    return title_blocks, plain_blocks


def title_check(content: str) -> bool:
    """正则匹配进行标题检查"""
    if content == '':
        return False
    regs = [
        re.compile('^(((第)|(附件))[一二三四五六七八九十1234567890\.]*)'),
        re.compile('^([^a-zA-Z\u4E00-\u9F5A]*[一二三四五六七八九十1234567890\.])'),
    ]
    regs = [
        re.compile('^\s*([一二三四五六七八九十]+)'),
        re.compile('^\s*第\s*([一二三四五六七八九十]+)\s*[条款章节]*'),
        re.compile('^\s*[\(\（\﹙\（\⁽]\s*([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]'),
        re.compile('^\s*([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]+'),
        re.compile('^\s*(\d+)'),
        re.compile('^\s*第\s*(\d+)\s*[条款章节]*'),
        re.compile('^\s*[\(\（\﹙\（\⁽]\s*(\d+)\s*[\)\）\﹚\）\⁾]'),
        re.compile('^\s*(\d+)\s*[\)\）\﹚\）\⁾]'),
        # re.compile('^\s*(\d+\.\d+)'),
        # re.compile('^\s*第\s*(\d+\.\d+)\s*[条款章节]*'),
        # re.compile('^\s*[\(\（\﹙\（\⁽]\s*(\d+\.\d+)\s*[\)\）\﹚\）\⁾]'),
        # re.compile('^\s*(\d+\.\d+)\s*[\)\）\﹚\）\⁾]'),
    ]
    for reg in regs:
        if reg.search(content) is not None:
            return True
    return False


def get_font_sizes(blocks: Dict) -> Dict:
    font_sizes = dict()
    for block_id, block in blocks.items():
        if 'textFont' in block:
            if 'fontSize' in block['textFont']:
                font_sizes[block_id] = block['textFont']['fontSize']
    return font_sizes


def get_font_bolds(blocks: Dict) -> Dict:
    bolds = dict()
    for block_id, block in blocks.items():
        if 'textFont' in block:
            if 'bold' in block['textFont']:
                bolds[block_id] = 1 if block['textFont']['bold'] else 0
    return bolds


def font_check(title_font_sizes: Dict, plain_font_sizes: Dict, filer_threshold:int = 2) -> bool:
    """
    检验 title_blocks 的 font_sizes 是否显著偏高于plain_blocks
    """
    if len(title_font_sizes) <= filer_threshold:
        return False
    if len(plain_font_sizes) <= filer_threshold:
        return False

    # 比较中位的font_size
    if np.median(list(title_font_sizes.values())) >= 1 + np.median(list(plain_font_sizes.values())):
        return True
    else:
        return False


def bold_check(title_font_bolds: Dict, plain_font_bolds: Dict, filer_threshold: int = 2) -> bool:
    """
    检验 title_blocks 的 bolds 是否显著偏高于plain_blocks
    """
    if len(title_font_bolds) <= filer_threshold:
        return False
    if len(plain_font_bolds) <= filer_threshold:
        return False

    # 比较中位的font_bold
    if np.median(list(title_font_bolds.values())) > 0.5 + np.median(list(plain_font_bolds.values())):
        return True
    else:
        return False


def parse_toc(contract_data: Dict):
    """
    第一步：找出可能的title
    第二步：按照font_size、 font_bold过滤title
    第三步：按字序区分title层级
    第四步：检查字序是否缺失，若缺失则再查找title
    第五步：检查title是否完整，若不完整则补全
    """
    title_blocks, plain_blocks = divide_blocks(contract_data)

    # 按font_size 过滤 title, 超过一半的 title 字号高于 普通文本才过滤。
    title_font_sizes = get_font_sizes(title_blocks)
    plain_font_sizes = get_font_sizes(plain_blocks)
    filer_threshold = int(len(title_blocks) * 0.5)
    if font_check(title_font_sizes, plain_font_sizes, filer_threshold):
        plain_median_font_size = np.median(plain_font_sizes.values())
        title_block_ids = list(title_blocks.keys())
        for block_id in title_block_ids:
            if title_font_sizes.get(block_id, 0) < 1 +  plain_median_font_size:
                title_blocks.pop(block_id)

    # 按font_bold 过滤 title, 超过一半的 title 是粗体才过滤。
    title_font_bolds = get_font_bolds(title_blocks)
    plain_font_bolds = get_font_bolds(plain_blocks)
    filer_threshold = int(len(title_blocks) * 0.5)
    if bold_check(title_font_bolds, plain_font_bolds, filer_threshold):
        plain_median_font_bold = np.median(plain_font_bolds.values())
        title_block_ids = list(title_blocks.keys())
        for block_id in title_block_ids:
            if title_font_bolds.get(block_id, 0) < 0.5 + plain_median_font_bold:
                title_blocks.pop(block_id)

    # 标题序号检查
    number_patterns = [
        re.compile('^\s*([一二三四五六七八九十]+)'),
        re.compile('^\s*第\s*([一二三四五六七八九十]+)\s*[条款章节]*'),
        re.compile('^\s*[\(\（\﹙\（\⁽]\s*([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]'),
        re.compile('^\s*([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]+'),
        re.compile('^\s*(\d+)'),
        re.compile('^\s*第\s*(\d+)\s*[条款章节]*'),
        re.compile('^\s*[\(\（\﹙\（\⁽]\s*(\d+)\s*[\)\）\﹚\）\⁾]'),
        re.compile('^\s*(\d+)\s*[\)\）\﹚\）\⁾]'),
        # re.compile('^\s*(\d+\.\d+)'),
        # re.compile('^\s*第\s*(\d+\.\d+)\s*[条款章节]*'),
        # re.compile('^\s*[\(\（\﹙\（\⁽]\s*(\d+\.\d+)\s*[\)\）\﹚\）\⁾]'),
        # re.compile('^\s*(\d+\.\d+)\s*[\)\）\﹚\）\⁾]'),
    ]
    all_missed = []
    for pattern in number_patterns:
        missed = title_number_check(title_blocks, pattern)
        all_missed.extend(missed)

    # 这里的确可以检查出来的。
    for missed in all_missed:
        print(missed)


def title_number_check(title_blocks: Dict, number_pattern: re.Pattern) -> List:
    """
    检查字序是否缺失，若缺失则返回缺失的段落区间及序号模式
    """
    number_labels = []
    zh_pattern = re.compile('[一二三四五六七八九十]')
    for block_id, block in title_blocks.items():
        reg = number_pattern.search(block['content'])
        if reg is None:
            continue
        matched_text = reg.group(1)
        if zh_pattern.search(matched_text) is not None:
            number = cn2an.cn2an(matched_text)
        else:
            number = int(matched_text)
        number_labels.append((number, block))

    missed = []
    for idx in range(len(number_labels) - 1):
        cur_item = number_labels[idx]
        cur_number = cur_item[0]
        next_item = number_labels[idx + 1]
        next_numer = next_item[0]
        if 1 < next_numer - cur_number < 5:
            missed.append((cur_item, next_item, number_pattern))
    return missed


if __name__ == '__main__':
    file = r'/data/11.12按法务意见修改稿  钦州大榄坪作业区1-3号泊位污水处理站周边雨污水泵送等机电设备采购安装合同-.json'
    # file = r'D:\myworks\gitlab\contract-review\data\1、中国建设银行股票回购增持贷款合同（4份，公章+法人章）.json'
    with open(file, 'r', encoding='utf-8') as fs:
        data = json.load(fs)

    parse_toc(data)
